package com.spring.factory;

import com.spring.annotation.*;
import com.spring.aop.DataSourceHandler;
import com.spring.aop.TransactionHandler;
import com.spring.aop.TransactionManager;

import java.lang.reflect.*;
import java.sql.Connection;
import java.util.*;

public abstract class AbstractBeanFactory implements BeanFactory {
    private AbstractBeanFactory() {
    }

    /*
    二级缓存池
     */
    private static Map<String, Object> leyerObjects = new HashMap<>();

    private static Set<String> transactionalClass = new HashSet<>();

    private static List<String> removeTag = new ArrayList<>();

    private static DefaultSingletonBeanRegistry defaultSingletonBeanRegistry;

    private static AbstractBeanFactory abstractBeanFactory = new AbstractBeanFactory() {
    };

    public static AbstractBeanFactory getAbstractBeanFactory() {
        defaultSingletonBeanRegistry = DefaultSingletonBeanRegistry.getInstance();
        return abstractBeanFactory;
    }

    @Override
    public <T> T getBean(String beanId) {
        return (T) defaultSingletonBeanRegistry.getSingleton(beanId);
    }

    @Override
    public <T> T getBean(String beanId, Class<T> clazz) {
        return null;
    }

    @Override
    public <T> T getBean(Class<T> clazz) {
        Collection<Object> singletonAll = defaultSingletonBeanRegistry.getSingletonAll();
        for (Object obj : singletonAll) {
            if (obj.getClass() == clazz) {
                return (T) obj;
            }
            Class<?> superclass = obj.getClass().getSuperclass();
            if (superclass == clazz) {
                return (T) obj;
            }
            Class<?>[] interfaces = obj.getClass().getInterfaces();
            for (Class<?> anInterface : interfaces) {
                if (anInterface == clazz) {
                    return (T) obj;
                }
            }
        }
        return null;
    }

    public void beanRegister(Set<Class<?>> classSet) throws Exception {
        for (Class<?> claxx : classSet) {
            if (claxx.getAnnotation(Configuration.class) != null
                    || claxx.getAnnotation(Service.class) != null
                    || claxx.getAnnotation(Component.class) != null
                    || claxx.getAnnotation(Repository.class) != null) {
                doGetBean(claxx);
            }

        }
        for (Object value : leyerObjects.values()) {
            doGetBean(value.getClass());
        }
        for (String s : removeTag) {
            leyerObjects.remove(s);
        }
        for (String className : transactionalClass) {
            Object bean = getBean(className);
            Class<?> aClass = bean.getClass();
            DataSourceHandler dataSourceHandler = getBean(DataSourceHandler.class);
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field field : declaredFields) {
                if (field.getAnnotation(Autowired.class) != null) {
                    Object bean1 = getBean(field.getType());
                    if (bean1 != null) {
                        field.setAccessible(true);
                        field.set(bean,bean1);
                    }
                }
            }
            Object agencyObject = getAgencyObject(bean, dataSourceHandler);
            System.out.println(agencyObject);
            defaultSingletonBeanRegistry.registerSingleton(className, agencyObject);
        }
    }

    public void doGetBean(Class<?> claxx) throws IllegalAccessException, InstantiationException,
            InvocationTargetException {
        // 获取需要注入的对象
        String simpleName = claxx.getSimpleName();
        String className = lowerFirstChar(simpleName);
        Field[] declaredFields = claxx.getDeclaredFields();
        Object instance = null;
        Transactional claxxAnnotation = claxx.getAnnotation(Transactional.class);
        Transactional methodAnnotation = null;
        // 获取是否有使用事务控制，如果有用事务则生成代理对象
        Method[] declaredMethods = claxx.getDeclaredMethods();
        for (Method method : declaredMethods) {
            Transactional annotation = method.getAnnotation(Transactional.class);
            if (annotation != null) {
                methodAnnotation = annotation;
            }
        }
        if (claxxAnnotation != null || methodAnnotation != null) {
            transactionalClass.add(className);
            // 生成代理对象
            DataSourceHandler dataSourceHandler = getBean(DataSourceHandler.class);
            if (dataSourceHandler == null) {
                instance = claxx.newInstance();
            } else {
                instance = claxx.newInstance();
            }
        } else {
            instance = claxx.newInstance();
        }
        // 进行属性注入，如果有属性注入不成功 当前对象存入二级缓存
        if (declaredFields == null || declaredFields.length == 0) {
            defaultSingletonBeanRegistry.registerSingleton(className, instance);
        } else {
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                Autowired annotation = declaredField.getAnnotation(Autowired.class);
                if (annotation != null) {
                    // 获取需要注入的bean
                    Object object = getObject(declaredField);
                    // 如果获取不到，则将对象存入二级缓存中暴露
                    if (object == null) {
                        leyerObjects.put(className, instance);
                        return;
                    } else {
                        declaredField.set(instance, object);
                    }
                }
            }
            // 只有获取不到对应对象才会跳出方法，执行到这则直接注册到单例池中
//            leyerObjects.remove(className);
            removeTag.add(className);
            defaultSingletonBeanRegistry.registerSingleton(className, instance);
        }
        methodBeanRegister(claxx);
    }

    public Object getObject(Field declaredField) {
        // 获取对应的对象，先从二级缓存取
        Object learyBean = getLearyBean(declaredField.getName());
        if (learyBean == null) {
            learyBean = getLearyBean(declaredField.getType());
        }
        // 判断如果二级获取的也是空 则从一级缓存取
        if (learyBean == null) {
            learyBean = getBean(declaredField.getName());
        }
        if (learyBean == null) {
            learyBean = getBean(declaredField.getType());
        }
        return learyBean;
    }

    public Object getObject(Class<?> type) {
        Object learyBean = getLearyBean(type);
        if (learyBean == null) {
            learyBean = getBean(type);
        }
        return learyBean;
    }

    /**
     * 实现@Bean方式注入bean
     *
     * @param type
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public void methodBeanRegister(Class<?> type) throws InvocationTargetException, IllegalAccessException {
        Method[] methods = type.getDeclaredMethods();
        if (methods == null || methods.length == 0) {
            return;
        }
        for (Method method : methods) {
            Bean methodAnnotation = method.getAnnotation(Bean.class);
            if (methodAnnotation != null) {
                //获取返回值类型
                Class<?> returnType = method.getReturnType();
                Parameter[] parameters = method.getParameters();
                Object bean = getObject(type);
                Object invoke = method.invoke(bean, parameters);
                String simpleName = returnType.getSimpleName();
                String className = lowerFirstChar(simpleName);
                defaultSingletonBeanRegistry.registerSingleton(className, invoke);
            }
        }
    }

    private String lowerFirstChar(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    private TransactionManager getTransactionManager(Connection connection) {
        TransactionManager transactionManager = TransactionManager.getInstance();
        transactionManager.setConnection(connection);
        return transactionManager;
    }

    public Object getAgencyObject(Object object, DataSourceHandler dataSourceHandler) throws IllegalAccessException,
            InstantiationException {
        Connection connection = dataSourceHandler.getConnection();
        TransactionManager transactionManager = getTransactionManager(connection);
        TransactionHandler transactionHandler = new TransactionHandler(object, transactionManager);
        Object proxy = transactionHandler.getProxy();
        return proxy;
    }

    private Object getLearyBean(String name) {
        return leyerObjects.get(name);
    }

    private Object getLearyBean(Class<?> type) {
        Collection<Object> values = leyerObjects.values();
        for (Object obj : values) {
            if (obj.getClass() == type) {
                return obj;
            }
            Class<?> superclass = obj.getClass().getSuperclass();
            if (superclass == type) {
                return obj;
            }
            Class<?>[] interfaces = obj.getClass().getInterfaces();
            for (Class<?> anInterface : interfaces) {
                if (anInterface == type) {
                    return obj;
                }
            }
        }
        return null;
    }
}
